home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 1999 #2 / Amiga Plus CD - 1999 - No. 2.iso / System-Boost / Workbench / ToolManager / Source / Library / exec.c < prev    next >
C/C++ Source or Header  |  1998-06-17  |  15KB  |  576 lines

  1. /*
  2.  * exec.c  V3.1
  3.  *
  4.  * ToolManager Objects Exec class
  5.  *
  6.  * Copyright (C) 1990-98 Stefan Becker
  7.  *
  8.  * This source code is for educational purposes only. You may study it
  9.  * and copy ideas or algorithms from it for your own projects. It is
  10.  * not allowed to use any of the source codes (in full or in parts)
  11.  * in other programs. Especially it is not allowed to create variants
  12.  * of ToolManager or ToolManager-like programs from this source code.
  13.  *
  14.  */
  15.  
  16. #include "toolmanager.h"
  17.  
  18. /* Global data */
  19. const char DefaultOutput[]    = "NIL:";
  20. const char DefaultDirectory[] = "SYS:";
  21.  
  22. /* Hook function prototype */
  23. typedef ULONG (*HookFuncPtr)(__A0 struct Hook *, __A1 void *, __A2 void *);
  24.  
  25. /* Local data */
  26. #define PROPCHUNKS 6
  27. static const ULONG PropChunkTable[2 * PROPCHUNKS] = {
  28.  ID_TMEX, ID_CDIR,
  29.  ID_TMEX, ID_CMND,
  30.  ID_TMEX, ID_HKEY,
  31.  ID_TMEX, ID_OUTP,
  32.  ID_TMEX, ID_PATH,
  33.  ID_TMEX, ID_PSCR
  34. };
  35. static const struct TagItem TagsToFlags[] = {
  36.  TMOP_Arguments, DATA_EXECF_ARGUMENTS,
  37.  TMOP_ToFront,   DATA_EXECF_TOFRONT,
  38.  TAG_DONE
  39. };
  40.  
  41. /* Exec class instance data */
  42. struct ExecClassData {
  43.  ULONG                 ecd_Flags;
  44.  UWORD                 ecd_ExecType;
  45.  WORD                  ecd_Priority;
  46.  ULONG                 ecd_Stack;
  47.  char                 *ecd_CurrentDir;
  48.  char                 *ecd_Command;
  49.  char                 *ecd_Output;
  50.  char                 *ecd_Path;
  51.  char                 *ecd_PubScreen;
  52.  char                **ecd_PathArray;
  53.  CxObj                *ecd_HotKey;
  54.  struct TMMemberData  *ecd_DockLink;
  55. };
  56. #define TYPED_INST_DATA(cl, o) ((struct ExecClassData *) INST_DATA((cl), (o)))
  57.  
  58. /* Flags for strings allocated in IFF parsing */
  59. #define IFFF_CURRENTDIR 0x80000000  /* ecd_CurrentDir          */
  60. #define IFFF_COMMAND    0x40000000  /* ecd_Command             */
  61. #define IFFF_OUTPUT     0x20000000  /* ecd_Output              */
  62. #define IFFF_PATH       0x10000000  /* ecd_Path                */
  63. #define IFFF_PUBSCREEN  0x08000000  /* ecd_PubScreen           */
  64.  
  65. /* Convert comma seperated path list to array of string pointers */
  66. #undef  DEBUGFUNCTION
  67. #define DEBUGFUNCTION ConvertPathList
  68. static char **ConvertPathList(char *s)
  69. {
  70.  ULONG   count = 0;
  71.  char  **rc    = NULL;
  72.  
  73.  EXECCLASS_LOG(LOG2(Paths, "%s (0x%08lx)", s, s))
  74.  
  75.  /* Count entries in string and add string terminators */
  76.  {
  77.   char *path = s;
  78.  
  79.   /* Scan path string */
  80.   while (path) {
  81.  
  82.    /* Find next entry and add string terminator to current entry */
  83.    if (path = strchr(path, ',')) *path = '\0';
  84.  
  85.    /* Increment counter */
  86.    count++;
  87.   }
  88.  }
  89.  
  90.  EXECCLASS_LOG(LOG1(Count, "%ld", count))
  91.  
  92.  /* Any entries in list? Allocate path array */
  93.  if (count && (rc = GetVector(sizeof(char *) * (count + 1)))) {
  94.   char **entry = rc;
  95.  
  96.   EXECCLASS_LOG(LOG1(PathArray, "0x%08lx", rc))
  97.  
  98.   /* For each entry */
  99.   while (count--) {
  100.  
  101.    EXECCLASS_LOG(LOG1(Next Entry, "%s", s))
  102.  
  103.    /* Store pointer to next path */
  104.    *entry++ = s;
  105.  
  106.    /* Go to next path (skip string terminator) */
  107.    s += strlen(s) + 1;
  108.   }
  109.  
  110.   /* Set array terminator */
  111.   *entry = NULL;
  112.  }
  113.  
  114.  EXECCLASS_LOG(LOG1(Result, "0x%08lx", rc))
  115.  
  116.  return(rc);
  117. }
  118.  
  119. /* Exec class method: OM_NEW */
  120. #undef  DEBUGFUNCTION
  121. #define DEBUGFUNCTION ExecClassNew
  122. static ULONG ExecClassNew(Class *cl, Object *obj, struct opSet *ops)
  123. {
  124.  EXECCLASS_LOG((LOG1(Tags, "0x%08lx", ops->ops_AttrList),
  125.                 PrintTagList(ops->ops_AttrList)))
  126.  
  127.  /* Call SuperClass */
  128.  if (obj = (Object *) DoSuperMethodA(cl, obj, (Msg) ops)) {
  129.   struct ExecClassData *ecd = TYPED_INST_DATA(cl, obj);
  130.  
  131.   /* Initialize instance data */
  132.   ecd->ecd_Flags     = 0;
  133.   ecd->ecd_Command   = NULL;
  134.   ecd->ecd_Output    = DefaultOutput;
  135.   ecd->ecd_PathArray = NULL;
  136.   ecd->ecd_HotKey    = NULL;
  137.   ecd->ecd_DockLink  = NULL;
  138.  }
  139.  
  140.  return((ULONG) obj);
  141. }
  142.  
  143. /* Exec class method: OM_DISPOSE */
  144. #undef  DEBUGFUNCTION
  145. #define DEBUGFUNCTION ExecClassDispose
  146. static ULONG ExecClassDispose(Class *cl, Object *obj, Msg msg)
  147. {
  148.  struct ExecClassData *ecd = TYPED_INST_DATA(cl, obj);
  149.  
  150.  EXECCLASS_LOG(LOG0(Disposing))
  151.  
  152.  /* Dock attached? Release it */
  153.  if (ecd->ecd_DockLink)
  154.   DoMethod(ecd->ecd_DockLink->tmmd_Object, TMM_Release, ecd->ecd_DockLink);
  155.  
  156.  /* Hotkey allocated? */
  157.  if (ecd->ecd_HotKey) SafeDeleteCxObjAll(ecd->ecd_HotKey, obj);
  158.  
  159.  /* Path array allocated? */
  160.  if (ecd->ecd_PathArray) FreeVector(ecd->ecd_PathArray);
  161.  
  162.  /* Free IFF data */
  163.  if (ecd->ecd_Flags & IFFF_PUBSCREEN)  FreeVector(ecd->ecd_PubScreen);
  164.  if (ecd->ecd_Flags & IFFF_PATH)       FreeVector(ecd->ecd_Path);
  165.  if (ecd->ecd_Flags & IFFF_OUTPUT)     FreeVector(ecd->ecd_Output);
  166.  if (ecd->ecd_Flags & IFFF_COMMAND)    FreeVector(ecd->ecd_Command);
  167.  if (ecd->ecd_Flags & IFFF_CURRENTDIR) FreeVector(ecd->ecd_CurrentDir);
  168.  
  169.  /* Call SuperClass */
  170.  return(DoSuperMethodA(cl, obj, msg));
  171. }
  172.  
  173. /* Exec class method: TMM_Release */
  174. #undef  DEBUGFUNCTION
  175. #define DEBUGFUNCTION ExecClassRelease
  176. static ULONG ExecClassRelease(Class *cl, Object *obj, struct TMP_Detach *tmpd)
  177. {
  178.  struct ExecClassData *ecd = TYPED_INST_DATA(cl, obj);
  179.  
  180.  EXECCLASS_LOG(LOG2(Arguments, "Data 0x%08lx Object 0x%08lx",
  181.                     tmpd->tmpd_MemberData, tmpd->tmpd_MemberData->tmmd_Object))
  182.  
  183.  /* Detach dock */
  184.  DoMethod(ecd->ecd_DockLink->tmmd_Object, TMM_Detach, ecd->ecd_DockLink);
  185.  ecd->ecd_DockLink = NULL;
  186.  
  187.  /* Return 1 to indicate that the method is implemented */
  188.  return(1);
  189. }
  190.  
  191. /* Exec class method: TMM_ParseIFF */
  192. #undef  DEBUGFUNCTION
  193. #define DEBUGFUNCTION ExecClassParseIFF
  194. static ULONG ExecClassParseIFF(Class *cl, Object *obj,
  195.                                struct TMP_ParseIFF *tmppi)
  196. {
  197.  BOOL rc = FALSE;
  198.  
  199.  EXECCLASS_LOG(LOG1(Handle, "0x%08lx", tmppi->tmppi_IFFHandle))
  200.  
  201.  /* Initialize IFF parser and forward method to SuperClass */
  202.  if ((PropChunks(tmppi->tmppi_IFFHandle, PropChunkTable, PROPCHUNKS) == 0) &&
  203.      DoSuperMethodA(cl, obj, (Msg) tmppi)) {
  204.   struct StoredProperty *sp;
  205.  
  206.   EXECCLASS_LOG(LOG0(FORM TMEX chunk parsed OK))
  207.  
  208.   /* Check for mandatory DATA property */
  209.   if (sp = FindProp(tmppi->tmppi_IFFHandle, ID_TMEX, ID_DATA)) {
  210.    struct ExecClassData *ecd = TYPED_INST_DATA(cl, obj);
  211.    struct ExecDATAChunk *edc = sp->sp_Data;
  212.  
  213.    EXECCLASS_LOG(LOG4(Data, "Flags 0x%08lx Type %ld Prio %ld Stack %ld",
  214.                       edc->edc_Standard.sdc_Flags, edc->edc_ExecType,
  215.                       edc->edc_Priority, edc->edc_Stack))
  216.  
  217.    /* Initialize class data */
  218.    ecd->ecd_Flags    = edc->edc_Standard.sdc_Flags & DATA_EXECF_MASK;
  219.    ecd->ecd_ExecType = edc->edc_ExecType;
  220.    ecd->ecd_Priority = edc->edc_Priority;
  221.    ecd->ecd_Stack    = edc->edc_Stack;
  222.  
  223.    /* Duplicate strings and set flags */
  224.    if (ecd->ecd_CurrentDir = DuplicateProperty(tmppi->tmppi_IFFHandle,
  225.                                                ID_TMEX, ID_CDIR))
  226.     ecd->ecd_Flags |= IFFF_CURRENTDIR;
  227.    else
  228.     ecd->ecd_CurrentDir    = GetGlobalDefaultDirectory();
  229.    if (ecd->ecd_Command    = DuplicateProperty(tmppi->tmppi_IFFHandle,
  230.                                                ID_TMEX, ID_CMND))
  231.     ecd->ecd_Flags |= IFFF_COMMAND;
  232.    if (ecd->ecd_Output     = DuplicateProperty(tmppi->tmppi_IFFHandle,
  233.                                                ID_TMEX, ID_OUTP))
  234.     ecd->ecd_Flags |= IFFF_OUTPUT;
  235.    else
  236.     ecd->ecd_Output        = DefaultOutput;
  237.    if (ecd->ecd_Path       = DuplicateProperty(tmppi->tmppi_IFFHandle,
  238.                                                ID_TMEX, ID_PATH))
  239.     ecd->ecd_Flags |= IFFF_PATH;
  240.    if (ecd->ecd_PubScreen  = DuplicateProperty(tmppi->tmppi_IFFHandle,
  241.                                                ID_TMEX, ID_PSCR))
  242.     ecd->ecd_Flags |= IFFF_PUBSCREEN;
  243.  
  244.    /* Convert path string to path array */
  245.    ecd->ecd_PathArray = ConvertPathList(ecd->ecd_Path);
  246.  
  247.    /* HotKey specified? */
  248.    if (sp = FindProp(tmppi->tmppi_IFFHandle, ID_TMEX, ID_HKEY))
  249.  
  250.     /* Yes, reate Hotkey */
  251.     ecd->ecd_HotKey = CreateHotKey(sp->sp_Data, obj);
  252.  
  253.    /* Configuration data parsed */
  254.    rc = TRUE;
  255.   }
  256.  }
  257.  
  258.  EXECCLASS_LOG(LOG1(Result, "%ld", rc))
  259.  
  260.  return(rc);
  261. }
  262.  
  263. /* Exec class method: TMM_ParseTags */
  264. #undef  DEBUGFUNCTION
  265. #define DEBUGFUNCTION ExecClassParseTags
  266. static ULONG ExecClassParseTags(Class *cl, Object *obj,
  267.                                 struct TMP_ParseTags *tmppt)
  268. {
  269.  struct ExecClassData *ecd    = TYPED_INST_DATA(cl, obj);
  270.  struct TagItem       *tstate = tmppt->tmppt_Tags;
  271.  struct TagItem       *ti;
  272.  BOOL                  rc     = TRUE;
  273.  
  274.  EXECCLASS_LOG((LOG1(Tags, "0x%08lx", tmppt->tmppt_Tags),
  275.                 PrintTagList(tmppt->tmppt_Tags)))
  276.  
  277.  /* Scan tag list */
  278.  while (rc && (ti = NextTagItem(&tstate)))
  279.  
  280.   /* Which tag? */
  281.   switch (ti->ti_Tag) {
  282.    case TMOP_Command:
  283.     if (ti->ti_Data)
  284.  
  285.      ecd->ecd_Command = (char *) ti->ti_Data;
  286.  
  287.     else
  288.  
  289.      rc = FALSE;
  290.  
  291.     break;
  292.  
  293.    case TMOP_CurrentDir:
  294.     if (ti->ti_Data)
  295.  
  296.      /* Set new current directory */
  297.      ecd->ecd_CurrentDir = (char *) ti->ti_Data;
  298.  
  299.     else
  300.  
  301.      /* Set to default value */
  302.      ecd->ecd_CurrentDir = DefaultDirectory;
  303.  
  304.     break;
  305.  
  306.    case TMOP_ExecType: {
  307.      UWORD type = ti->ti_Data;
  308.  
  309.      /* Sanity check */
  310.      if ((type <= TMET_Network) || (type == TMET_Hook))
  311.  
  312.       /* Type OK */
  313.       ecd->ecd_ExecType = type;
  314.  
  315.      else
  316.  
  317.       /* Error */
  318.       rc = FALSE;
  319.     }
  320.     break;
  321.  
  322.    case TMOP_HotKey:
  323.     /* Free old hotkey */
  324.     if (ecd->ecd_HotKey) SafeDeleteCxObjAll(ecd->ecd_HotKey, obj);
  325.  
  326.     /* String valid? */
  327.     if (ti->ti_Data)
  328.  
  329.      /* Yes, create new hotkey */
  330.      rc = (ecd->ecd_HotKey = CreateHotKey((char *) ti->ti_Data, obj)) != NULL;
  331.  
  332.     else
  333.  
  334.      /* Hotkey cleared */
  335.      ecd->ecd_HotKey = NULL;
  336.  
  337.     break;
  338.  
  339.    case TMOP_Output:
  340.     if (ti->ti_Data)
  341.  
  342.      /* Set new output file */
  343.      ecd->ecd_Output = (char *) ti->ti_Data;
  344.  
  345.     else
  346.  
  347.      /* Reset to default output file */
  348.      ecd->ecd_Output = DefaultOutput;
  349.  
  350.     break;
  351.  
  352.    case TMOP_Path:
  353.     /* Free old path */
  354.     if (ecd->ecd_Flags & IFFF_PATH) FreeVector(ecd->ecd_Path);
  355.  
  356.     /* Free old path array */
  357.     if (ecd->ecd_PathArray) FreeVector(ecd->ecd_PathArray);
  358.  
  359.     /* String valid? Duplicate it */
  360.     if (ti->ti_Data &&
  361.         (ecd->ecd_Path = GetVector(strlen((char *) ti->ti_Data) + 1))) {
  362.  
  363.      /* Copy string */
  364.      strcpy(ecd->ecd_Path, (char *) ti->ti_Data);
  365.  
  366.      /* Set flag */
  367.      ecd->ecd_Flags |= IFFF_PATH;
  368.  
  369.      /* Convert path string */
  370.      rc = (ecd->ecd_PathArray = ConvertPathList(ecd->ecd_Path)) != NULL;
  371.  
  372.     } else {
  373.  
  374.      /* Path cleared */
  375.      ecd->ecd_Flags     &= ~IFFF_PATH;
  376.      ecd->ecd_PathArray  = NULL;
  377.     }
  378.     break;
  379.  
  380.    case TMOP_Priority:
  381.     ecd->ecd_Priority = ti->ti_Data;
  382.     break;
  383.  
  384.    case TMOP_PubScreen:
  385.     ecd->ecd_PubScreen = (char *) ti->ti_Data;
  386.     break;
  387.  
  388.    case TMOP_Stack:
  389.     ecd->ecd_Stack = ti->ti_Data;
  390.     break;
  391.   }
  392.  
  393.  /* Set flags if no error */
  394.  if (rc) ecd->ecd_Flags = PackBoolTags(ecd->ecd_Flags, tmppt->tmppt_Tags,
  395.                                        TagsToFlags);
  396.  
  397.  EXECCLASS_LOG(LOG1(Result, "%ld", rc))
  398.  
  399.  return(rc);
  400. }
  401.  
  402. /* Exec class method: TMM_Activate */
  403. #undef  DEBUGFUNCTION
  404. #define DEBUGFUNCTION ExecClassActivate
  405. static ULONG ExecClassActivate(Class *cl, Object *obj,
  406.                                struct TMP_Activate *tmpa)
  407. {
  408.  struct ExecClassData *ecd = TYPED_INST_DATA(cl, obj);
  409.  BOOL                  rc  = FALSE;
  410.  
  411.  EXECCLASS_LOG(LOG1(Data, "0x%08lx", tmpa->tmpa_Data))
  412.  
  413.  /* Check if command is valid */
  414.  if (ecd->ecd_Command) {
  415.  
  416.   /* Check ToFront flag */
  417.   if (ecd->ecd_Flags & DATA_EXECF_TOFRONT) {
  418.    struct Screen *s;
  419.  
  420.    /* Flag set, lock public screen */
  421.    if (s = LockPubScreen(ecd->ecd_PubScreen)) {
  422.  
  423.     /* Move screen to front */
  424.     ScreenToFront(s);
  425.  
  426.     /* Unlock screen */
  427.     UnlockPubScreen(NULL, s);
  428.    }
  429.   }
  430.  
  431.   /* Which exec type? */
  432.   switch (ecd->ecd_ExecType) {
  433.    case TMET_CLI:
  434.     rc = StartCLIProgram(ecd->ecd_Command, ecd->ecd_CurrentDir,
  435.                          ecd->ecd_PathArray, ecd->ecd_Output, ecd->ecd_Stack,
  436.                          ecd->ecd_Priority,
  437.                          (ecd->ecd_Flags & DATA_EXECF_ARGUMENTS) ?
  438.                           tmpa->tmpa_Data : NULL);
  439.     break;
  440.  
  441.    case TMET_WB:
  442.     rc = StartWBProgram(ecd->ecd_Command, ecd->ecd_CurrentDir, ecd->ecd_Stack,
  443.                         ecd->ecd_Priority,
  444.                         (ecd->ecd_Flags & DATA_EXECF_ARGUMENTS) ?
  445.                          tmpa->tmpa_Data : NULL);
  446.     break;
  447.  
  448.    case TMET_ARexx:
  449.     rc = StartARexxProgram(ecd->ecd_Command, ecd->ecd_CurrentDir,
  450.                            (ecd->ecd_Flags & DATA_EXECF_ARGUMENTS) ?
  451.                             tmpa->tmpa_Data : NULL);
  452.     break;
  453.  
  454.    case TMET_Dock: {
  455.      struct TMHandle *tmh;
  456.      Object          *dock;
  457.  
  458.      /* Get TMHandle */
  459.      GetAttr(TMA_TMHandle, obj, (ULONG *) &tmh);
  460.  
  461.      /* Find dock */
  462.      if (dock = FindTypedNamedTMObject(tmh, ecd->ecd_Command,
  463.                                        TMOBJTYPE_DOCK)) {
  464.  
  465.       EXECCLASS_LOG(LOG1(Dock, "0x%08lx", dock))
  466.  
  467.       /* Send activate message */
  468.       DoMethod(dock, TMM_Activate, NULL);
  469.  
  470.       /* All OK */
  471.       rc = TRUE;
  472.      }
  473.     }
  474.     break;
  475.  
  476.    case TMET_HotKey:
  477.     rc = SendInputEvent(ecd->ecd_Command);
  478.     break;
  479.  
  480.    case TMET_Network:
  481.     /* NOT SUPPORTED YET! */
  482.     break;
  483.  
  484.    case TMET_Hook: {
  485.      struct Hook *hook = (struct Hook *) ecd->ecd_Command;
  486.  
  487.      EXECCLASS_LOG(LOG3(Call Hook, "Hook 0x%08lx Entry 0x%08lx Data 0x%08lx",
  488.                         hook, hook->h_Entry, hook->h_Data))
  489.  
  490.      /* Call hook function. Calling conventions:          */
  491.      /* A0 (hook)   : pointer to hook structure           */
  492.      /* A1 (message): pointer to AppMessage (may be NULL) */
  493.      /* A2 (object) : value of hook->h_Data               */
  494.      /* Return Code : BOOL, FALSE for failure             */
  495.      rc = ((HookFuncPtr) hook->h_Entry)(hook, tmpa->tmpa_Data, hook->h_Data);
  496.     }
  497.     break;
  498.  
  499.   }
  500.  }
  501.  
  502.  /* Program started? */
  503.  if (rc == FALSE) DisplayBeep(NULL);
  504.  
  505.  /* Return 1 to indicate that the method is implemented */
  506.  return(1);
  507. }
  508.  
  509. /* Exec class dispatcher */
  510. #undef  DEBUGFUNCTION
  511. #define DEBUGFUNCTION ExecClassDispatcher
  512. static __geta4 ULONG ExecClassDispatcher(__A0 Class *cl, __A2 Object *obj,
  513.                                          __A1 Msg msg)
  514. {
  515.  ULONG rc;
  516.  
  517.  EXECCLASS_LOG(LOG3(Arguments, "Class 0x%08lx Object 0x%08lx Msg 0x%08lx",
  518.                     cl, obj, msg))
  519.  
  520.  switch(msg->MethodID) {
  521.   /* BOOPSI methods */
  522.   case OM_NEW:
  523.    rc = ExecClassNew(cl, obj, (struct opSet *) msg);
  524.    break;
  525.  
  526.   case OM_DISPOSE:
  527.    rc = ExecClassDispose(cl, obj, msg);
  528.    break;
  529.  
  530.   /* TM methods */
  531.   case TMM_Release:
  532.    rc = ExecClassRelease(cl, obj, (struct TMP_Detach *) msg);
  533.    break;
  534.  
  535.   case TMM_ParseIFF:
  536.    rc = ExecClassParseIFF(cl, obj, (struct TMP_ParseIFF *) msg);
  537.    break;
  538.  
  539.   case TMM_ParseTags:
  540.    rc = ExecClassParseTags(cl, obj, (struct TMP_ParseTags *) msg);
  541.    break;
  542.  
  543.   case TMM_Activate:
  544.    rc = ExecClassActivate(cl, obj, (struct TMP_Activate *) msg);
  545.    break;
  546.  
  547.   /* Unknown method -> delegate to SuperClass */
  548.   default:
  549.    rc = DoSuperMethodA(cl, obj, msg);
  550.    break;
  551.  }
  552.  
  553.  return(rc);
  554. }
  555.  
  556. /* Create Exec class */
  557. #undef  DEBUGFUNCTION
  558. #define DEBUGFUNCTION CreateExecClass
  559. Class *CreateExecClass(Class *superclass)
  560. {
  561.  Class *cl;
  562.  
  563.  EXECCLASS_LOG(LOG1(SuperClass, "0x%08lx", superclass))
  564.  
  565.  /* Create class */
  566.  if (cl = MakeClass(NULL, NULL, superclass, sizeof(struct ExecClassData), 0))
  567.  
  568.   /* Set dispatcher */
  569.   cl->cl_Dispatcher.h_Entry = (ULONG (*)()) ExecClassDispatcher;
  570.  
  571.  EXECCLASS_LOG(LOG1(Class, "0x%08lx", cl))
  572.  
  573.  /* Return pointer to class */
  574.  return(cl);
  575. }
  576.